package com.oig.sys.minio.core;

import com.oig.sys.minio.config.MinIOProperties;
import com.oig.sys.minio.util.ObjectNameUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
public class MinIOTemplate  {

    private final MinioClient minioClient;
    private final MinIOProperties minIOProperties;

    /**
     * 如果临时桶不存在则创建
     */
    public MinIOTemplate(MinIOProperties minIOProperties) {
        this.minIOProperties = minIOProperties;
        this.minioClient = MinioClient.builder()
                .endpoint(minIOProperties.getEndpoint())
                .credentials(minIOProperties.getAccessKey(), minIOProperties.getSecretKey())
                .build();
        this.createBucket(minIOProperties.getTempBucket());
    }


    /**
     * 检查文件存储桶是否存在
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName){
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public void createBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 获取全部bucket
     * <p>
     * https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
     */
    @SneakyThrows
    public List<Bucket> getAllBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 根据bucketName获取信息
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public Optional<Bucket> getBucket(String bucketName) {
        return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除信息
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 根据文件前缀查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return MinioItem 列表
     */
    @SneakyThrows
    public List<MinIOItem> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        List<MinIOItem> objectList = new ArrayList<>();
        Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
        for (Result<Item> result : objectsIterator) {
            objectList.add(new MinIOItem(result.get()));
        }
        return objectList;
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    过期时间 <=7
     * @return url
     */
    @SneakyThrows
    public String getPutObjectURL(String bucketName, String objectName, Integer expires) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(bucketName).object(objectName).expiry(expires).build());
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return url
     */
    @SneakyThrows
    public String getObjectURL(String bucketName, String objectName) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).build());
    }

    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName) {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 上传临时文件
     *
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public String putObject(String objectName, InputStream stream) throws Exception {
        return this.putObject(minIOProperties.getTempBucket(), objectName, stream);
    }

    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public String putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        StringBuilder destObjectName = new StringBuilder() ;
        if (minIOProperties.getOpenDateDir()){
            destObjectName.append(ObjectNameUtil.dateDirYMD()).append(ObjectNameUtil.FILE_SPLIT_PATH);
        }
        destObjectName.append(objectName);
        minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(destObjectName.toString())
                        .contentType("application/octet-stream")
                        .stream(stream,-1, 10485760)
                        .build());
        return bucketName+ObjectNameUtil.FILE_SPLIT_PATH+destObjectName;
    }


    /**
     * 上传文件
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contextType 类型
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public String putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
        StringBuilder destObjectName = new StringBuilder() ;
        if (minIOProperties.getOpenDateDir()){
            destObjectName.append(ObjectNameUtil.dateDirYMD()).append(ObjectNameUtil.FILE_SPLIT_PATH);
        }
        destObjectName.append(objectName);
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(destObjectName.toString())
                .contentType(contextType)
                .stream(stream,size, -1)
                .build());
        return bucketName+ObjectNameUtil.FILE_SPLIT_PATH+destObjectName;
    }



    /**
     * copy文件，挪桶
     *
     * @param srcBucketName     源bucket名称
     * @param srcObjectName     源文件名称
     * @param destBucketName    目标bucket名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public String copyObject(String srcBucketName, String srcObjectName, String destBucketName, String destObjectName) throws Exception {
        log.info("srcBucketName:{},srcObjectName:{},destBucketName:{},destObjectName:{}",srcBucketName,srcObjectName,destBucketName,destObjectName);
        this.createBucket(destBucketName);
        minioClient.copyObject(CopyObjectArgs.builder()
                .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build())
                .bucket(destBucketName)
                .object(srcObjectName)
                .build());
        return destBucketName+ObjectNameUtil.FILE_SPLIT_PATH+destObjectName;
    }

    /**
     * 临时桶文件，挪到业务桶
     *
     * @param srcObjectName     源文件名称
     * @param destBucketName    目标bucket名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public String copyAndDelTempObject(String srcObjectName, String destBucketName) throws Exception {
        log.info("srcObjectName:{},destBucketName:{}",srcObjectName,destBucketName);
        this.createBucket(destBucketName);
        minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(minIOProperties.getTempBucket()).object(srcObjectName).build())
                .bucket(destBucketName)
                .object(srcObjectName)
                .build());
        log.info("已经把文件从临时桶移动到业务桶=====srcObjectName{}destBucketName{}",srcObjectName,destBucketName);
        this.removeObject(minIOProperties.getTempBucket(), srcObjectName);
        return destBucketName+ObjectNameUtil.FILE_SPLIT_PATH+srcObjectName;
    }


    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
     */
    public void removeObject(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }



    /**
     * 临时桶
     */
    public String getTempBucket(){
        return minIOProperties.getTempBucket();
    }

    /**
     * 文件是否存在桶
     * @param bucketName 桶
     * @param objectName 文件名
     * @return 是否存在
     */
    public boolean objectNameExists(String bucketName,String objectName) {
        InputStream inputStream = null;
        try {
            // 尝试获取文件的输入流
            inputStream=this.getObject(bucketName, objectName);
            return true;
        } catch (Exception e) {
            // 文件不存在或发生其他异常
            return false;
        }finally {
            IOUtils.closeQuietly(inputStream);
        }
    }
}
